//----------------------------------------------------------------------------------------------------------------------
// Copyright (c) 2012 James Whitworth
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//----------------------------------------------------------------------------------------------------------------------
#include <gmock/gmock.h>
//----------------------------------------------------------------------------------------------------------------------
#include <sqbind/sqbTableIterator.h>
//----------------------------------------------------------------------------------------------------------------------
#include "fixtures/TypeFixture.h"
#include "StringHelpers.h"
//----------------------------------------------------------------------------------------------------------------------

//----------------------------------------------------------------------------------------------------------------------
typedef SquirrelFixture TableIteratorTest;


//----------------------------------------------------------------------------------------------------------------------
template<typename TypeParam>
class TableIteratorTypedValueTest : public BaseTypeFixture<TypeParam>
{
public:
  enum {
    kSlotCount = 10,
  };

  HSQOBJECT     m_table;
  const SQChar *m_keys[kSlotCount];
  TypeParam    *m_values[kSlotCount];

  SQBIND_BEGIN_NONSTANDARD_EXTENSION_BLOCK();

  virtual void SetUp() SQBIND_OVERRIDE;
  virtual void TearDown() SQBIND_OVERRIDE;

  SQBIND_END_NONSTANDARD_EXTENSION_BLOCK();
};

//----------------------------------------------------------------------------------------------------------------------
template<typename TypeParam>
void TableIteratorTypedValueTest<TypeParam>::SetUp()
{
  BaseTypeFixture<TypeParam>::SetUp();

  for (size_t i = 0; i != kSlotCount; ++i)
  {
    m_keys[i] = nullptr;
    m_values[i] = nullptr;
  }

  sq_newtable(m_vm);
  for (size_t i = 0; i != kSlotCount; ++i)
  {
    // ensure all keys are unique
    //
    for (;;)
    {
      bool unique = true;

      const SQChar* key = GetRandomString();
      for (size_t j = 0; j != i; ++j)
      {
        if (scstrcmp(key, m_keys[j]) == 0)
        {
          unique = false;
          break;
        }
      }

      if (unique)
      {
        m_keys[i] = key;
        break;
      }
    }
    
    m_values[i] = new TypeParam(GetRandomValue());

    sq_pushstring(m_vm, m_keys[i], -1);
    ASSERT_SQ_SUCCEEDED(m_vm, sqb::Push(m_vm, *m_values[i]));
    ASSERT_SQ_SUCCEEDED(m_vm, sq_rawset(m_vm, -3));
  }

  sq_getstackobj(m_vm, -1, &m_table);
  sq_addref(m_vm, &m_table);

  sq_poptop(m_vm);
}

//----------------------------------------------------------------------------------------------------------------------
template<typename TypeParam>
void TableIteratorTypedValueTest<TypeParam>::TearDown()
{
  sq_release(m_vm, &m_table);

  for (size_t i = 0; i != kSlotCount; ++i)
  {
    delete m_values[i];
  }

  BaseTypeFixture<TypeParam>::TearDown();
}

TYPED_TEST_CASE(TableIteratorTypedValueTest, AllTypes);

//----------------------------------------------------------------------------------------------------------------------
template<typename TypeParam>
class TableIteratorTypedKeyTest : public BaseTypeFixture<TypeParam> {};
TYPED_TEST_CASE(TableIteratorTypedKeyTest, NativeTypes);

//----------------------------------------------------------------------------------------------------------------------
TEST_F(TableIteratorTest, TestEmptyTableIteration)
{
  sq_newtable(m_vm);

  {
    sqb::TableIterator iterator(m_vm, -1);
    EXPECT_FALSE(iterator.Next());
  }

  sq_poptop(m_vm);
}

//----------------------------------------------------------------------------------------------------------------------
TYPED_TEST(TableIteratorTypedValueTest, TestIteration)
{
  sqb::TableIterator iterator(m_vm, m_table);
  
  size_t count = 0;
  for (size_t i = 0; i != kSlotCount; ++i)
  {
    EXPECT_TRUE(iterator.Next());

    EXPECT_EQ(OT_STRING, iterator.GetKeyType());

    const SQChar* key;
    iterator.GetKey(&key);

    bool found = false;
    for (size_t j = 0; j != kSlotCount; ++j)
    {
      if (scstrcmp(key, m_keys[j]) == 0)
      {
        found = true;

        TypeParam value = GetRandomValue();
        iterator.GetValue(&value);
        EXPECT_TYPE_PARAM_EQ(*m_values[j], value);

        break;
      }
    }
    EXPECT_TRUE(found);
  }
  EXPECT_FALSE(iterator.Next());
}
